#define IS_CALL_RELOC(R_TYPE) \
((R_TYPE) == R_LARCH_B26 \
- ||(R_TYPE) == R_LARCH_CALL36)
+ ||(R_TYPE) == R_LARCH_CALL36 \
+ ||(R_TYPE) == R_LARCH_CALL30)
/* If TLS GD/IE need dynamic relocations, INDX will be the dynamic indx,
and set NEED_RELOC to true used in allocate_dynrelocs and
bool bad_extern_access =
(bfd_link_pde (info)
|| r_type == R_LARCH_PCREL20_S2
- || r_type == R_LARCH_PCALA_HI20);
+ || r_type == R_LARCH_PCALA_HI20
+ || r_type == R_LARCH_PCADD_HI20);
if (h)
name = h->root.root.string;
switch (r_type)
{
case R_LARCH_GOT_PC_HI20:
+ case R_LARCH_GOT_PCADD_HI20:
case R_LARCH_GOT_HI20:
case R_LARCH_SOP_PUSH_GPREL:
/* For la.global. */
break;
case R_LARCH_TLS_LD_PC_HI20:
+ case R_LARCH_TLS_LD_PCADD_HI20:
case R_LARCH_TLS_LD_HI20:
case R_LARCH_TLS_GD_PC_HI20:
+ case R_LARCH_TLS_GD_PCADD_HI20:
case R_LARCH_TLS_GD_HI20:
case R_LARCH_SOP_PUSH_TLS_GD:
if (!loongarch_elf_record_tls_and_got_reference (abfd, info, h,
break;
case R_LARCH_TLS_IE_PC_HI20:
+ case R_LARCH_TLS_IE_PCADD_HI20:
case R_LARCH_TLS_IE_HI20:
case R_LARCH_SOP_PUSH_TLS_GOT:
if (bfd_link_pic (info))
break;
case R_LARCH_TLS_DESC_PC_HI20:
+ case R_LARCH_TLS_DESC_PCADD_HI20:
case R_LARCH_TLS_DESC_HI20:
if (!loongarch_elf_record_tls_and_got_reference (abfd, info, h,
r_symndx,
should not be used to build shared libraries.
In static PIE undefined weak symbols may be allowed
by rewriting pcaddi to addi.w if addend is in [-2048, 2048). */
+ case R_LARCH_PCADD_HI20:
case R_LARCH_PCREL20_S2:
if (bfd_link_pic (info)
&& (sec->flags & SEC_ALLOC) != 0
case R_LARCH_B21:
case R_LARCH_B26:
case R_LARCH_CALL36:
+ case R_LARCH_CALL30:
if (h != NULL)
{
h->needs_plt = 1;
case R_LARCH_TLS_LD_PCREL20_S2:
case R_LARCH_TLS_GD_PCREL20_S2:
case R_LARCH_TLS_DESC_PCREL20_S2:
+ case R_LARCH_CALL30:
+ case R_LARCH_PCADD_HI20:
+ case R_LARCH_PCADD_LO12:
+ case R_LARCH_GOT_PCADD_HI20:
+ case R_LARCH_GOT_PCADD_LO12:
+ case R_LARCH_TLS_IE_PCADD_HI20:
+ case R_LARCH_TLS_IE_PCADD_LO12:
+ case R_LARCH_TLS_LD_PCADD_HI20:
+ case R_LARCH_TLS_LD_PCADD_LO12:
+ case R_LARCH_TLS_GD_PCADD_HI20:
+ case R_LARCH_TLS_GD_PCADD_LO12:
+ case R_LARCH_TLS_DESC_PCADD_HI20:
+ case R_LARCH_TLS_DESC_PCADD_LO12:
r = loongarch_check_offset (rel, input_section);
if (r != bfd_reloc_ok)
break;
return addr - elf_hash_table (info)->tls_sec->vma;
}
+typedef struct
+{
+ /* PC value. */
+ bfd_vma address;
+ /* Relocation value with addend. */
+ bfd_vma value;
+} loongarch_pcrel_hi_reloc;
+
+typedef struct loongarch_pcrel_lo_reloc
+{
+ /* PC value of pcaddu12i. */
+ bfd_vma address;
+ /* Internal relocation. */
+ Elf_Internal_Rela *reloc;
+ /* loongarch_elf_relocate_section can only handle an input section at a time,
+ so we can only resolved pcadd_hi20 and pcadd_lo12 in the same section. If
+ these pcrel relocs are not in the same section we should report dangerous
+ relocation errors. */
+ asection *input_section;
+ struct bfd_link_info *info;
+ reloc_howto_type *howto;
+ bfd_byte *contents;
+ /* The next loongarch_pcrel_lo_reloc. */
+ struct loongarch_pcrel_lo_reloc *next;
+} loongarch_pcrel_lo_reloc;
+
+typedef struct
+{
+ /* Hash table for loongarch_pcrel_hi_reloc. */
+ htab_t hi_relocs;
+ /* Linked list for loongarch_pcrel_lo_reloc. */
+ loongarch_pcrel_lo_reloc *lo_relocs;
+} loongarch_pcrel_relocs;
+
+/* Hash function of the pcrel_hi_reloc hash table. */
+static hashval_t
+loongarch_pcrel_reloc_hash (const void *entry)
+{
+ const loongarch_pcrel_hi_reloc *e = entry;
+ return (hashval_t)(e->address >> 2);
+}
+
+/* Comparison function of the pcrel_hi_reloc hash table. */
+static int
+loongarch_pcrel_reloc_eq (const void *entry1, const void *entry2)
+{
+ const loongarch_pcrel_hi_reloc *e1 = entry1, *e2 = entry2;
+ return e1->address == e2->address;
+}
+
+static bool
+loongarch_init_pcrel_relocs (loongarch_pcrel_relocs *p)
+{
+ p->lo_relocs = NULL;
+ p->hi_relocs = htab_create (1024, loongarch_pcrel_reloc_hash,
+ loongarch_pcrel_reloc_eq, free);
+ return p->hi_relocs != NULL;
+}
+
+static void
+loongarch_free_pcrel_reloc (loongarch_pcrel_relocs *p)
+{
+ loongarch_pcrel_lo_reloc *cur = p->lo_relocs;
+
+ while (cur != NULL)
+ {
+ loongarch_pcrel_lo_reloc *next = cur->next;
+ free (cur);
+ cur = next;
+ }
+ htab_delete (p->hi_relocs);
+}
+
+static bool
+loongarch_record_pcrel_hi_reloc (loongarch_pcrel_relocs *p,
+ bfd_vma addr,
+ bfd_vma *value)
+{
+ bfd_vma offset = *value - addr;
+ bfd_vma off_lo = offset & (bfd_vma)0xfff;
+ /* If lo12 immediate > 0x7ff, because sign-extend caused by addi.w/ld.w,
+ hi20 immediate need to add 0x1.
+ See RELOCATE_CALC_PC32_HI20(relocation, pc) */
+ if (off_lo > 0x7ff)
+ offset += 0x1000;
+
+ *value = offset;
+
+ loongarch_pcrel_hi_reloc entry = {addr, offset};
+ loongarch_pcrel_hi_reloc **slot =
+ (loongarch_pcrel_hi_reloc **)htab_find_slot (p->hi_relocs, &entry, INSERT);
+
+ BFD_ASSERT (*slot == NULL);
+ *slot = (loongarch_pcrel_hi_reloc *) bfd_malloc (sizeof (loongarch_pcrel_hi_reloc));
+ if (*slot == NULL)
+ return false;
+ **slot = entry;
+ return true;
+}
+
+static bool
+loongarch_record_pcrel_lo_reloc (loongarch_pcrel_relocs *p,
+ bfd_vma addr,
+ Elf_Internal_Rela *reloc,
+ asection *input_section,
+ struct bfd_link_info *info,
+ reloc_howto_type *howto,
+ bfd_byte *contents)
+{
+ loongarch_pcrel_lo_reloc *entry;
+ entry = (loongarch_pcrel_lo_reloc *) bfd_malloc (sizeof (loongarch_pcrel_lo_reloc));
+ if (entry == NULL)
+ return false;
+ *entry = (loongarch_pcrel_lo_reloc) {addr, reloc, input_section, info,
+ howto, contents, p->lo_relocs};
+ p->lo_relocs = entry;
+ return true;
+}
+
+static bool
+loongarch_resolve_pcrel_lo_relocs (loongarch_pcrel_relocs *p)
+{
+ loongarch_pcrel_lo_reloc *r;
+ for (r = p->lo_relocs; r != NULL; r = r->next)
+ {
+ bfd *input_bfd = r->input_section->owner;
+ loongarch_pcrel_hi_reloc search = {r->address, 0};
+ loongarch_pcrel_hi_reloc *entry = htab_find (p->hi_relocs, &search);
+
+ char *string = NULL;
+ if (entry == NULL)
+ string = _("%pcrel_lo missing marching %pcrel_hi");
+
+ if (string != NULL)
+ {
+ ((*r->info->callbacks->reloc_overflow)
+ (r->info, NULL, NULL, r->howto->name, (bfd_vma) 0,
+ input_bfd, r->input_section, r->reloc->r_offset));
+ return true;
+ }
+
+ perform_relocation (r->reloc, r->input_section, r->howto, entry->value,
+ input_bfd, r->contents);
+ }
+ return true;
+}
+
static int
loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
bfd *input_bfd, asection *input_section,
{
Elf_Internal_Rela *rel;
Elf_Internal_Rela *relend;
+ loongarch_pcrel_relocs pcrel_relocs;
bool fatal = false;
asection *sreloc = elf_section_data (input_section)->sreloc;
struct loongarch_elf_link_hash_table *htab = loongarch_elf_hash_table (info);
bool is_dyn = elf_hash_table (info)->dynamic_sections_created;
asection *plt = htab->elf.splt ? htab->elf.splt : htab->elf.iplt;
asection *got = htab->elf.sgot;
+ uint32_t insn;
+
+ if (!loongarch_init_pcrel_relocs (&pcrel_relocs))
+ return false;
relend = relocs + input_section->reloc_count;
for (rel = relocs; rel < relend; rel++)
case R_LARCH_B21:
case R_LARCH_B26:
case R_LARCH_CALL36:
+ case R_LARCH_CALL30:
unresolved_reloc = false;
bool via_plt =
plt != NULL && h != NULL && h->plt.offset != (bfd_vma) - 1;
/* A call to an undefined weak symbol is converted to 0. */
if (!via_plt && IS_CALL_RELOC (r_type))
{
- /* call36 fn1 => pcaddu18i $ra,0+jirl $ra,$zero,0
- tail36 $t0,fn1 => pcaddi18i $t0,0+jirl $zero,$zero,0 */
- if (R_LARCH_CALL36 == r_type)
+ /* call36 fn1 => pcaddu18i $ra,0 + jirl $ra,$zero,0
+ tail36 $t0,fn1 => pcaddi18i $t0,0 + jirl $t0,$zero,0
+ call30 fn1 => pcaddu12i $ra,0 + jirl $ra,$zero,0
+ tail30 $t0,fn1 => pcaddi12i $t0,0 + jirl $t0,$zero,0 */
+ if (r_type == R_LARCH_CALL36
+ || r_type == R_LARCH_CALL30)
{
uint32_t jirl = bfd_get (32, input_bfd,
contents + rel->r_offset + 4);
if (LARCH_INSN_B (b_bl))
bfd_put (32, input_bfd, LARCH_OP_JIRL,
contents + rel->r_offset);
- else
- /* bl %plt(fn1) => jirl $ra,zero,0. */
- bfd_put (32, input_bfd, LARCH_OP_JIRL | 0x1,
- contents + rel->r_offset);
+ else /* bl %plt(fn1) => jirl $ra,zero,0. */
+ bfd_put (32, input_bfd, LARCH_OP_JIRL | 0x1,
+ contents + rel->r_offset);
}
r = bfd_reloc_continue;
break;
case R_LARCH_PCALA64_LO20:
pc -= 8;
/* Fall through. */
- case R_LARCH_PCREL20_S2:
case R_LARCH_PCALA_HI20:
+ case R_LARCH_PCREL20_S2:
unresolved_reloc = false;
/* If sym is undef weak and it's hidden or we are doing a static
break;
}
- uint32_t insn = bfd_get (32, input_bfd,
+ insn = bfd_get (32, input_bfd,
contents + rel->r_offset);
insn = LARCH_GET_RD (insn) | LARCH_OP_ADDI_W;
insn |= (relocation & 0xfff) << 10;
RELOCATE_CALC_PC32_HI20 (relocation, pc);
if (resolve_pcrel_undef_weak)
{
- uint32_t insn = bfd_get (32, input_bfd,
+ insn = bfd_get (32, input_bfd,
contents + rel->r_offset);
insn = LARCH_GET_RD (insn) | LARCH_OP_LU12I_W;
bfd_put_32 (input_bfd, insn, contents + rel->r_offset);
}
break;
- case R_LARCH_TLS_LE_HI20_R:
- relocation += rel->r_addend;
- relocation = tlsoff (info, relocation);
- RELOCATE_TLS_TP32_HI20 (relocation);
- break;
-
case R_LARCH_PCALA_LO12:
/* Not support if sym_addr in 2k page edge.
pcalau12i pc_hi20 (sym_addr)
/* For 2G jump, generate pcalau12i, jirl. */
/* If use jirl, turns to R_LARCH_B16. */
- uint32_t insn = bfd_get (32, input_bfd, contents + rel->r_offset);
+ insn = bfd_get (32, input_bfd, contents + rel->r_offset);
if (LARCH_INSN_JIRL (insn))
{
relocation &= 0xfff;
}
break;
+ case R_LARCH_PCADD_HI20:
+ resolve_pcrel_undef_weak =
+ ((info->nointerp
+ || (h && ELF_ST_VISIBILITY (h->other) != STV_DEFAULT))
+ && is_undefweak);
+ if (resolve_pcrel_undef_weak)
+ pc = 0;
+
+ if (h && h->plt.offset != MINUS_ONE)
+ relocation = sec_addr (plt) + h->plt.offset;
+ else
+ relocation += rel->r_addend;
+
+ if (!loongarch_record_pcrel_hi_reloc (&pcrel_relocs, pc, &relocation))
+ r = bfd_reloc_overflow;
+
+ if (resolve_pcrel_undef_weak)
+ {
+ insn = bfd_get (32, input_bfd,
+ contents + rel->r_offset);
+ insn = LARCH_GET_RD (insn) | LARCH_OP_LU12I_W;
+ bfd_put_32 (input_bfd, insn, contents + rel->r_offset);
+ }
+ break;
+
+ case R_LARCH_PCADD_LO12:
+ case R_LARCH_GOT_PCADD_LO12:
+ case R_LARCH_TLS_IE_PCADD_LO12:
+ case R_LARCH_TLS_LD_PCADD_LO12:
+ case R_LARCH_TLS_GD_PCADD_LO12:
+ case R_LARCH_TLS_DESC_PCADD_LO12:
+ if (loongarch_record_pcrel_lo_reloc (&pcrel_relocs, relocation, rel,
+ input_section, info, howto,
+ contents))
+ continue;
+ r = bfd_reloc_overflow;
+ break;
+
case R_LARCH_GOT_PC_HI20:
+ case R_LARCH_GOT_PCADD_HI20:
case R_LARCH_GOT_HI20:
/* Calc got offset. */
{
if (r_type == R_LARCH_GOT_PC_HI20)
RELOCATE_CALC_PC32_HI20 (relocation, pc);
+ if (r_type == R_LARCH_GOT_PCADD_HI20)
+ {
+ if (!loongarch_record_pcrel_hi_reloc (&pcrel_relocs, pc,
+ &relocation))
+ r = bfd_reloc_overflow;
+ }
break;
case R_LARCH_GOT_PC_LO12:
break;
+ case R_LARCH_TLS_LE_HI20_R:
+ relocation += rel->r_addend;
+ relocation = tlsoff (info, relocation);
+ RELOCATE_TLS_TP32_HI20 (relocation);
+ break;
+
case R_LARCH_TLS_LE_HI20:
case R_LARCH_TLS_LE_LO12:
case R_LARCH_TLS_LE_LO12_R:
Now, LD and GD is both GOT_TLS_GD type, LD seems to
can be omitted. */
case R_LARCH_TLS_IE_PC_HI20:
+ case R_LARCH_TLS_IE_PCADD_HI20:
case R_LARCH_TLS_IE_HI20:
case R_LARCH_TLS_LD_PC_HI20:
+ case R_LARCH_TLS_LD_PCADD_HI20:
case R_LARCH_TLS_LD_HI20:
case R_LARCH_TLS_GD_PC_HI20:
+ case R_LARCH_TLS_GD_PCADD_HI20:
case R_LARCH_TLS_GD_HI20:
case R_LARCH_TLS_DESC_PC_HI20:
+ case R_LARCH_TLS_DESC_PCADD_HI20:
case R_LARCH_TLS_DESC_HI20:
case R_LARCH_TLS_LD_PCREL20_S2:
case R_LARCH_TLS_GD_PCREL20_S2:
unresolved_reloc = false;
if (r_type == R_LARCH_TLS_IE_PC_HI20
+ || r_type == R_LARCH_TLS_IE_PCADD_HI20
|| r_type == R_LARCH_TLS_IE_HI20)
is_ie = true;
if (r_type == R_LARCH_TLS_DESC_PC_HI20
+ || r_type == R_LARCH_TLS_DESC_PCADD_HI20
|| r_type == R_LARCH_TLS_DESC_HI20
|| r_type == R_LARCH_TLS_DESC_PCREL20_S2)
is_desc = true;
|| r_type == R_LARCH_TLS_GD_PCREL20_S2
|| r_type == R_LARCH_TLS_DESC_PCREL20_S2)
relocation -= pc;
+ else if (r_type == R_LARCH_TLS_IE_PCADD_HI20
+ || r_type == R_LARCH_TLS_LD_PCADD_HI20
+ || r_type == R_LARCH_TLS_GD_PCADD_HI20
+ || r_type == R_LARCH_TLS_DESC_PCADD_HI20)
+ if (!loongarch_record_pcrel_hi_reloc (&pcrel_relocs, pc,
+ &relocation))
+ r = bfd_reloc_overflow;
/* else {} ABS relocations. */
break;
fatal = true;
}
+ bool ret = loongarch_resolve_pcrel_lo_relocs (&pcrel_relocs);
+ fatal = !ret;
+ loongarch_free_pcrel_reloc (&pcrel_relocs);
+
return !fatal;
}