0, /* Source Mask. */
0x0000ffff, /* Dest Mask. */
FALSE), /* PC relative offset? */
+
+ /* Marker relocs for TLS. */
+ HOWTO (R_MICROBLAZE_TLS,
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ bfd_elf_generic_reloc, /* special_function */
+ "R_MICROBLAZE_TLS", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ HOWTO (R_MICROBLAZE_TLSGD,
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ bfd_elf_generic_reloc, /* special_function */
+ "R_MICROBLAZE_TLSGD", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ HOWTO (R_MICROBLAZE_TLSLD,
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ bfd_elf_generic_reloc, /* special_function */
+ "R_MICROBLAZE_TLSLD", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Computes the load module index of the load module that contains the
+ definition of its TLS sym. */
+ HOWTO (R_MICROBLAZE_TLSDTPMOD32,
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ bfd_elf_generic_reloc, /* special_function */
+ "R_MICROBLAZE_TLSDTPMOD32", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Computes a dtv-relative displacement, the difference between the value
+ of sym+add and the base address of the thread-local storage block that
+ contains the definition of sym, minus 0x8000. Used for initializing GOT */
+ HOWTO (R_MICROBLAZE_TLSDTPREL32,
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ bfd_elf_generic_reloc, /* special_function */
+ "R_MICROBLAZE_TLSDTPREL32", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Computes a dtv-relative displacement, the difference between the value
+ of sym+add and the base address of the thread-local storage block that
+ contains the definition of sym, minus 0x8000. */
+ HOWTO (R_MICROBLAZE_TLSDTPREL64,
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ bfd_elf_generic_reloc, /* special_function */
+ "R_MICROBLAZE_TLSDTPREL64", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Computes a tp-relative displacement, the difference between the value of
+ sym+add and the value of the thread pointer (r13). */
+ HOWTO (R_MICROBLAZE_TLSGOTTPREL32,
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ bfd_elf_generic_reloc, /* special_function */
+ "R_MICROBLAZE_TLSGOTTPREL32", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Computes a tp-relative displacement, the difference between the value of
+ sym+add and the value of the thread pointer (r13). */
+ HOWTO (R_MICROBLAZE_TLSTPREL32,
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ bfd_elf_generic_reloc, /* special_function */
+ "R_MICROBLAZE_TLSTPREL32", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
};
#ifndef NUM_ELEM
case BFD_RELOC_MICROBLAZE_32_GOTOFF:
microblaze_reloc = R_MICROBLAZE_GOTOFF_32;
break;
+ case BFD_RELOC_MICROBLAZE_64_TLSGD:
+ microblaze_reloc = R_MICROBLAZE_TLSGD;
+ break;
+ case BFD_RELOC_MICROBLAZE_64_TLSLD:
+ microblaze_reloc = R_MICROBLAZE_TLSLD;
+ break;
+ case BFD_RELOC_MICROBLAZE_32_TLSDTPREL:
+ microblaze_reloc = R_MICROBLAZE_TLSDTPREL32;
+ break;
+ case BFD_RELOC_MICROBLAZE_64_TLSDTPREL:
+ microblaze_reloc = R_MICROBLAZE_TLSDTPREL64;
+ break;
+ case BFD_RELOC_MICROBLAZE_32_TLSDTPMOD:
+ microblaze_reloc = R_MICROBLAZE_TLSDTPMOD32;
+ break;
+ case BFD_RELOC_MICROBLAZE_64_TLSGOTTPREL:
+ microblaze_reloc = R_MICROBLAZE_TLSGOTTPREL32;
+ break;
+ case BFD_RELOC_MICROBLAZE_64_TLSTPREL:
+ microblaze_reloc = R_MICROBLAZE_TLSTPREL32;
+ break;
case BFD_RELOC_MICROBLAZE_COPY:
microblaze_reloc = R_MICROBLAZE_COPY;
break;
/* Track dynamic relocs copied for this symbol. */
struct elf32_mb_dyn_relocs *dyn_relocs;
+ /* TLS Reference Types for the symbol; Updated by check_relocs */
+#define TLS_GD 1 /* GD reloc. */
+#define TLS_LD 2 /* LD reloc. */
+#define TLS_TPREL 4 /* TPREL reloc, => IE. */
+#define TLS_DTPREL 8 /* DTPREL reloc, => LD. */
+#define TLS_TLS 16 /* Any TLS reloc. */
+ unsigned char tls_mask;
+
};
+#define IS_TLS_GD(x) (x == (TLS_TLS | TLS_GD))
+#define IS_TLS_LD(x) (x == (TLS_TLS | TLS_LD))
+#define IS_TLS_DTPREL(x) (x == (TLS_TLS | TLS_DTPREL))
+#define IS_TLS_NONE(x) (x == 0)
+
#define elf32_mb_hash_entry(ent) ((struct elf32_mb_link_hash_entry *)(ent))
/* ELF linker hash table. */
/* Small local sym to section mapping cache. */
struct sym_cache sym_sec;
+
+ /* TLS Local Dynamic GOT Entry */
+ union {
+ bfd_signed_vma refcount;
+ bfd_vma offset;
+ } tlsld_got;
};
+/* Nonzero if this section has TLS related relocations. */
+#define has_tls_reloc sec_flg0
+
/* Get the ELF linker hash table from a link_info structure. */
#define elf32_mb_hash_table(p) \
eh = (struct elf32_mb_link_hash_entry *) entry;
eh->dyn_relocs = NULL;
+ eh->tls_mask = 0;
}
return entry;
+ h->u.def.section->output_offset);
}
+static bfd_vma
+dtprel_base (struct bfd_link_info *info)
+{
+ /* If tls_sec is NULL, we should have signalled an error already. */
+ if (elf_hash_table (info)->tls_sec == NULL)
+ return 0;
+ return elf_hash_table (info)->tls_sec->vma;
+}
+
+/* The size of the thread control block. */
+#define TCB_SIZE 8
+
+/* Output a simple dynamic relocation into SRELOC. */
+
+static void
+microblaze_elf_output_dynamic_relocation (bfd *output_bfd,
+ asection *sreloc,
+ unsigned long reloc_index,
+ unsigned long indx,
+ int r_type,
+ bfd_vma offset,
+ bfd_vma addend)
+{
+
+ Elf_Internal_Rela rel;
+
+ rel.r_info = ELF32_R_INFO (indx, r_type);
+ rel.r_offset = offset;
+ rel.r_addend = addend;
+
+ bfd_elf32_swap_reloca_out (output_bfd, &rel,
+ (sreloc->contents + reloc_index * sizeof (Elf32_External_Rela)));
+}
+
/* This code is taken from elf32-m32r.c
There is some attempt to make this function usable for many architectures,
both USE_REL and USE_RELA ['twould be nice if such a critter existed],
bfd_boolean ret = TRUE;
asection *sreloc;
bfd_vma *local_got_offsets;
+ unsigned int tls_type;
if (!microblaze_elf_howto_table[R_MICROBLAZE_max-1])
microblaze_elf_howto_init ();
h = NULL;
r_type = ELF32_R_TYPE (rel->r_info);
+ tls_type = 0;
+
if (r_type < 0 || r_type >= (int) R_MICROBLAZE_max)
{
(*_bfd_error_handler) (_("%s: unknown relocation type %d"),
break;
}
+ case (int) R_MICROBLAZE_TLSGD:
+ tls_type = (TLS_TLS | TLS_GD);
+ goto dogot;
+ case (int) R_MICROBLAZE_TLSLD:
+ tls_type = (TLS_TLS | TLS_LD);
+ dogot:
case (int) R_MICROBLAZE_GOT_64:
{
+ bfd_vma *offp;
+ bfd_vma off, off2;
+ unsigned long indx;
+ bfd_vma static_value;
+
+ bfd_boolean need_relocs = FALSE;
if (htab->sgot == NULL)
abort ();
- if (h == NULL)
+
+ indx = 0;
+ offp = NULL;
+
+ /* 1. Identify GOT Offset;
+ 2. Compute Static Values
+ 3. Process Module Id, Process Offset
+ 4. Fixup Relocation with GOT offset value. */
+
+ /* 1. Determine GOT Offset to use : TLS_LD, global, local */
+ if (IS_TLS_LD (tls_type))
+ offp = &htab->tlsld_got.offset;
+ else if (h != NULL)
+ {
+ if (htab->sgotplt != NULL && h->got.offset != (bfd_vma) -1)
+ offp = &h->got.offset;
+ else
+ abort ();
+ }
+ else
{
- bfd_vma off;
if (local_got_offsets == NULL)
abort ();
- off = local_got_offsets[r_symndx];
- /* The LSB indicates whether we've already
- created relocation. */
- if (off & 1)
- off &= ~1;
- else
- {
- bfd_put_32 (output_bfd, relocation + addend,
- htab->sgot->contents + off);
+ offp = &local_got_offsets[r_symndx];
+ }
+
+ if (!offp)
+ abort ();
+
+ off = (*offp) & ~1;
+ off2 = off;
+
+ if (IS_TLS_LD(tls_type) || IS_TLS_GD(tls_type))
+ off2 = off + 4;
+
+ /* Symbol index to use for relocs */
+ if (h != NULL)
+ {
+ bfd_boolean dyn =
+ elf_hash_table (info)->dynamic_sections_created;
+
+ if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared, h)
+ && (!info->shared || !SYMBOL_REFERENCES_LOCAL (info, h)))
+ indx = h->dynindx;
+ }
- if (info->shared)
+ /* Need to generate relocs ? */
+ if ((info->shared || indx != 0)
+ && (h == NULL
+ || ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
+ || h->root.type != bfd_link_hash_undefweak))
+ need_relocs = TRUE;
+
+ /* 2. Compute/Emit Static value of r-expression */
+ static_value = relocation + addend;
+
+ /* 3. Process module-id and offset */
+ if (! ((*offp) & 1) )
+ {
+ bfd_vma got_offset;
+
+ got_offset = (htab->sgot->output_section->vma
+ + htab->sgot->output_offset
+ + off);
+
+ /* Process module-id */
+ if (IS_TLS_LD(tls_type))
+ {
+ if (! info->shared)
{
- Elf_Internal_Rela outrel;
- bfd_byte *loc;
- if (htab->srelgot == NULL)
- abort ();
- outrel.r_offset = (htab->sgot->output_section->vma
- + htab->sgot->output_offset
- + off);
- outrel.r_info = ELF32_R_INFO (0, R_MICROBLAZE_REL);
- outrel.r_addend = relocation + addend;
- loc = htab->srelgot->contents;
- loc += htab->srelgot->reloc_count++
- * sizeof (Elf32_External_Rela);
- bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc);
+ bfd_put_32 (output_bfd, 1, htab->sgot->contents + off);
+ }
+ else
+ {
+ microblaze_elf_output_dynamic_relocation (output_bfd,
+ htab->srelgot, htab->srelgot->reloc_count++,
+ /* symindex= */ 0, R_MICROBLAZE_TLSDTPMOD32,
+ got_offset, 0);
}
- local_got_offsets[r_symndx] |= 1;
}
- relocation = htab->sgot->output_section->vma
- + htab->sgot->output_offset + off
- - htab->sgotplt->output_section->vma
- - htab->sgotplt->output_offset;
- unresolved_reloc = FALSE;
- }
- else
- {
- if (htab->sgotplt != NULL && h != NULL
- && h->got.offset != (bfd_vma) -1)
+ else if (IS_TLS_GD(tls_type))
+ {
+ if (! need_relocs)
+ {
+ bfd_put_32 (output_bfd, 1, htab->sgot->contents + off);
+ }
+ else
+ {
+ microblaze_elf_output_dynamic_relocation (output_bfd,
+ htab->srelgot,
+ htab->srelgot->reloc_count++,
+ /* symindex= */ indx, R_MICROBLAZE_TLSDTPMOD32,
+ got_offset, indx ? 0 : static_value);
+ }
+ }
+
+ /* Process Offset */
+ if (htab->srelgot == NULL)
+ abort ();
+
+ got_offset = (htab->sgot->output_section->vma
+ + htab->sgot->output_offset
+ + off2);
+ if (IS_TLS_LD(tls_type))
+ {
+ /* For LD, offset should be 0 */
+ *offp |= 1;
+ bfd_put_32 (output_bfd, 0, htab->sgot->contents + off2);
+ }
+ else if (IS_TLS_GD(tls_type))
{
- bfd_put_32 (output_bfd, relocation + addend,
- htab->sgot->contents + h->got.offset);
- relocation = htab->sgot->output_section->vma
- + htab->sgot->output_offset
- + h->got.offset
- - htab->sgotplt->output_section->vma
- - htab->sgotplt->output_offset;
- unresolved_reloc = FALSE;
+ *offp |= 1;
+ static_value -= dtprel_base(info);
+ if (need_relocs)
+ {
+ microblaze_elf_output_dynamic_relocation (output_bfd,
+ htab->srelgot, htab->srelgot->reloc_count++,
+ /* symindex= */ indx, R_MICROBLAZE_TLSDTPREL32,
+ got_offset, indx ? 0 : static_value);
+ }
+ else
+ {
+ bfd_put_32 (output_bfd, static_value,
+ htab->sgot->contents + off2);
+ }
}
else
- abort (); /* ??? */
+ {
+ bfd_put_32 (output_bfd, static_value,
+ htab->sgot->contents + off2);
+
+ /* Relocs for dyn symbols generated by
+ finish_dynamic_symbols */
+ if (info->shared && h == NULL)
+ {
+ *offp |= 1;
+ microblaze_elf_output_dynamic_relocation (output_bfd,
+ htab->srelgot, htab->srelgot->reloc_count++,
+ /* symindex= */ indx, R_MICROBLAZE_REL,
+ got_offset, static_value);
+ }
+ }
}
+
+ /* 4. Fixup Relocation with GOT offset value
+ Compute relative address of GOT entry for applying
+ the current relocation */
+ relocation = htab->sgot->output_section->vma
+ + htab->sgot->output_offset
+ + off
+ - htab->sgotplt->output_section->vma
+ - htab->sgotplt->output_offset;
+
+ /* Apply Current Relocation */
bfd_put_16 (input_bfd, (relocation >> 16) & 0xffff,
contents + offset + endian);
bfd_put_16 (input_bfd, relocation & 0xffff,
contents + offset + endian + INST_WORD_SIZE);
+
+ unresolved_reloc = FALSE;
break;
}
break;
}
+ case (int) R_MICROBLAZE_TLSDTPREL64:
+ relocation += addend;
+ relocation -= dtprel_base(info);
+ bfd_put_16 (input_bfd, (relocation >> 16) & 0xffff,
+ contents + offset + 2);
+ bfd_put_16 (input_bfd, relocation & 0xffff,
+ contents + offset + 2 + INST_WORD_SIZE);
+ break;
case (int) R_MICROBLAZE_64_PCREL :
case (int) R_MICROBLAZE_64:
case (int) R_MICROBLAZE_32:
return TRUE;
}
+static bfd_boolean
+update_local_sym_info (bfd *abfd,
+ Elf_Internal_Shdr *symtab_hdr,
+ unsigned long r_symndx,
+ unsigned int tls_type)
+{
+ bfd_signed_vma *local_got_refcounts = elf_local_got_refcounts (abfd);
+ unsigned char *local_got_tls_masks;
+
+ if (local_got_refcounts == NULL)
+ {
+ bfd_size_type size = symtab_hdr->sh_info;
+
+ size *= (sizeof (*local_got_refcounts) + sizeof (*local_got_tls_masks));
+ local_got_refcounts = bfd_zalloc (abfd, size);
+ if (local_got_refcounts == NULL)
+ return FALSE;
+ elf_local_got_refcounts (abfd) = local_got_refcounts;
+ }
+
+ local_got_tls_masks =
+ (unsigned char *) (local_got_refcounts + symtab_hdr->sh_info);
+ local_got_tls_masks[r_symndx] |= tls_type;
+ local_got_refcounts[r_symndx] += 1;
+
+ return TRUE;
+}
/* Look through the relocs for a section during the first phase. */
static bfd_boolean
unsigned int r_type;
struct elf_link_hash_entry * h;
unsigned long r_symndx;
+ unsigned char tls_type = 0;
r_symndx = ELF32_R_SYM (rel->r_info);
r_type = ELF32_R_TYPE (rel->r_info);
break;
/* This relocation requires .got entry. */
+ case R_MICROBLAZE_TLSGD:
+ tls_type |= (TLS_TLS | TLS_GD);
+ goto dogottls;
+ case R_MICROBLAZE_TLSLD:
+ tls_type |= (TLS_TLS | TLS_LD);
+ dogottls:
+ sec->has_tls_reloc = 1;
case R_MICROBLAZE_GOT_64:
if (htab->sgot == NULL)
{
if (h != NULL)
{
h->got.refcount += 1;
+ elf32_mb_hash_entry (h)->tls_mask |= tls_type;
}
else
{
- bfd_signed_vma *local_got_refcounts;
-
- /* This is a global offset table entry for a local symbol. */
- local_got_refcounts = elf_local_got_refcounts (abfd);
- if (local_got_refcounts == NULL)
- {
- bfd_size_type size;
-
- size = symtab_hdr->sh_info;
- size *= sizeof (bfd_signed_vma);
- local_got_refcounts = bfd_zalloc (abfd, size);
- if (local_got_refcounts == NULL)
- return FALSE;
- elf_local_got_refcounts (abfd) = local_got_refcounts;
- }
- local_got_refcounts[r_symndx] += 1;
+ if (! update_local_sym_info(abfd, symtab_hdr, r_symndx, tls_type) )
+ return FALSE;
}
break;
if (sreloc == NULL)
{
- const char *name;
bfd *dynobj;
- unsigned int strndx = elf_elfheader (abfd)->e_shstrndx;
- unsigned int shnam = _bfd_elf_single_rel_hdr (sec)->sh_name;
-
- name = bfd_elf_string_from_elf_section (abfd, strndx, shnam);
- if (name == NULL)
- return FALSE;
-
- if (strncmp (name, ".rela", 5) != 0
- || strcmp (bfd_get_section_name (abfd, sec),
- name + 5) != 0)
- {
- (*_bfd_error_handler)
- (_("%B: bad relocation section name `%s\'"),
- abfd, name);
- }
if (htab->elf.dynobj == NULL)
htab->elf.dynobj = abfd;
dynobj = htab->elf.dynobj;
- sreloc = bfd_get_linker_section (dynobj, name);
+ sreloc = _bfd_elf_make_dynamic_reloc_section (sec, dynobj,
+ 2, abfd, 1);
if (sreloc == NULL)
- {
- flagword flags;
-
- flags = (SEC_HAS_CONTENTS | SEC_READONLY
- | SEC_IN_MEMORY | SEC_LINKER_CREATED);
- if ((sec->flags & SEC_ALLOC) != 0)
- flags |= SEC_ALLOC | SEC_LOAD;
- sreloc = bfd_make_section_anyway_with_flags (dynobj,
- name,
- flags);
- if (sreloc == NULL
- || ! bfd_set_section_alignment (dynobj, sreloc, 2))
- return FALSE;
- }
- elf_section_data (sec)->sreloc = sreloc;
+ return FALSE;
}
/* If this is a global symbol, we count the number of
eind->dyn_relocs = NULL;
}
+ edir->tls_mask |= eind->tls_mask;
+
_bfd_elf_link_hash_copy_indirect (info, dir, ind);
}
h->needs_plt = 0;
}
+ eh = (struct elf32_mb_link_hash_entry *) h;
if (h->got.refcount > 0)
{
+ unsigned int need;
asection *s;
/* Make sure this symbol is output as a dynamic symbol.
return FALSE;
}
- s = htab->sgot;
- h->got.offset = s->size;
- s->size += 4;
- htab->srelgot->size += sizeof (Elf32_External_Rela);
+ need = 0;
+ if ((eh->tls_mask & TLS_TLS) != 0)
+ {
+ /* Handle TLS Symbol */
+ if ((eh->tls_mask & TLS_LD) != 0)
+ {
+ if (!eh->elf.def_dynamic)
+ /* We'll just use htab->tlsld_got.offset. This should
+ always be the case. It's a little odd if we have
+ a local dynamic reloc against a non-local symbol. */
+ htab->tlsld_got.refcount += 1;
+ else
+ need += 8;
+ }
+ if ((eh->tls_mask & TLS_GD) != 0)
+ need += 8;
+ }
+ else
+ {
+ /* Regular (non-TLS) symbol */
+ need += 4;
+ }
+ if (need == 0)
+ {
+ h->got.offset = (bfd_vma) -1;
+ }
+ else
+ {
+ s = htab->sgot;
+ h->got.offset = s->size;
+ s->size += need;
+ htab->srelgot->size += need * (sizeof (Elf32_External_Rela) / 4);
+ }
}
else
h->got.offset = (bfd_vma) -1;
- eh = (struct elf32_mb_link_hash_entry *) h;
if (eh->dyn_relocs == NULL)
return TRUE;
bfd_signed_vma *end_local_got;
bfd_size_type locsymcount;
Elf_Internal_Shdr *symtab_hdr;
+ unsigned char *lgot_masks;
asection *srel;
if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour)
symtab_hdr = &elf_tdata (ibfd)->symtab_hdr;
locsymcount = symtab_hdr->sh_info;
end_local_got = local_got + locsymcount;
+ lgot_masks = (unsigned char *) end_local_got;
s = htab->sgot;
srel = htab->srelgot;
- for (; local_got < end_local_got; ++local_got)
- {
- if (*local_got > 0)
- {
- *local_got = s->size;
- s->size += 4;
- if (info->shared)
- srel->size += sizeof (Elf32_External_Rela);
+ for (; local_got < end_local_got; ++local_got, ++lgot_masks)
+ {
+ if (*local_got > 0)
+ {
+ unsigned int need = 0;
+ if ((*lgot_masks & TLS_TLS) != 0)
+ {
+ if ((*lgot_masks & TLS_GD) != 0)
+ need += 8;
+ if ((*lgot_masks & TLS_LD) != 0)
+ htab->tlsld_got.refcount += 1;
+ }
+ else
+ need += 4;
+
+ if (need == 0)
+ {
+ *local_got = (bfd_vma) -1;
+ }
+ else
+ {
+ *local_got = s->size;
+ s->size += need;
+ if (info->shared)
+ srel->size += need * (sizeof (Elf32_External_Rela) / 4);
+ }
}
else
*local_got = (bfd_vma) -1;
sym dynamic relocs. */
elf_link_hash_traverse (elf_hash_table (info), allocate_dynrelocs, info);
+ if (htab->tlsld_got.refcount > 0)
+ {
+ htab->tlsld_got.offset = htab->sgot->size;
+ htab->sgot->size += 8;
+ if (info->shared)
+ htab->srelgot->size += sizeof (Elf32_External_Rela);
+ }
+ else
+ htab->tlsld_got.offset = (bfd_vma) -1;
+
if (elf_hash_table (info)->dynamic_sections_created)
{
/* Make space for the trailing nop in .plt. */
Elf_Internal_Sym *sym)
{
struct elf32_mb_link_hash_table *htab;
+ struct elf32_mb_link_hash_entry *eh = elf32_mb_hash_entry(h);
htab = elf32_mb_hash_table (info);
if (htab == NULL)
}
}
- if (h->got.offset != (bfd_vma) -1)
+ /* h->got.refcount to be checked ? */
+ if (h->got.offset != (bfd_vma) -1 &&
+ ! ((h->got.offset & 1) ||
+ IS_TLS_LD(eh->tls_mask) || IS_TLS_GD(eh->tls_mask)))
{
asection *sgot;
asection *srela;
- Elf_Internal_Rela rela;
- bfd_byte *loc;
+ bfd_vma offset;
/* This symbol has an entry in the global offset table. Set it
up. */
srela = htab->srelgot;
BFD_ASSERT (sgot != NULL && srela != NULL);
- rela.r_offset = (sgot->output_section->vma
- + sgot->output_offset
+ offset = (sgot->output_section->vma + sgot->output_offset
+ (h->got.offset &~ (bfd_vma) 1));
/* If this is a -Bsymbolic link, and the symbol is defined
&& h->def_regular)
{
asection *sec = h->root.u.def.section;
- rela.r_info = ELF32_R_INFO (0, R_MICROBLAZE_REL);
- rela.r_addend = (h->root.u.def.value
- + sec->output_section->vma
- + sec->output_offset);
+ microblaze_elf_output_dynamic_relocation (output_bfd,
+ srela, srela->reloc_count++,
+ /* symindex= */ 0,
+ R_MICROBLAZE_REL, offset,
+ h->root.u.def.value
+ + sec->output_section->vma
+ + sec->output_offset);
}
else
{
- rela.r_info = ELF32_R_INFO (h->dynindx, R_MICROBLAZE_GLOB_DAT);
- rela.r_addend = 0;
+ microblaze_elf_output_dynamic_relocation (output_bfd,
+ srela, srela->reloc_count++,
+ h->dynindx,
+ R_MICROBLAZE_GLOB_DAT,
+ offset, 0);
}
bfd_put_32 (output_bfd, (bfd_vma) 0,
sgot->contents + (h->got.offset &~ (bfd_vma) 1));
- loc = srela->contents;
- loc += srela->reloc_count++ * sizeof (Elf32_External_Rela);
- bfd_elf32_swap_reloca_out (output_bfd, &rela, loc);
}
if (h->needs_copy)
#define ELF_TARGET_ID MICROBLAZE_ELF_DATA
#define ELF_MACHINE_CODE EM_MICROBLAZE
#define ELF_MACHINE_ALT1 EM_MICROBLAZE_OLD
-#define ELF_MAXPAGESIZE 0x4 /* 4k, if we ever have 'em. */
+#define ELF_MAXPAGESIZE 0x1000
#define elf_info_to_howto microblaze_elf_info_to_howto
#define elf_info_to_howto_rel NULL
#define GOT_OFFSET 8
#define PLT_OFFSET 9
#define GOTOFF_OFFSET 10
-
+#define TLSGD_OFFSET 11
+#define TLSLD_OFFSET 12
+#define TLSDTPMOD_OFFSET 13
+#define TLSDTPREL_OFFSET 14
+#define TLSGOTTPREL_OFFSET 15
+#define TLSTPREL_OFFSET 16
/* Initialize the relax table. */
const relax_typeS md_relax_table[] =
{ 0x7fffffff, 0x80000000, INST_WORD_SIZE*2, 0 }, /* 8: GOT_OFFSET. */
{ 0x7fffffff, 0x80000000, INST_WORD_SIZE*2, 0 }, /* 9: PLT_OFFSET. */
{ 0x7fffffff, 0x80000000, INST_WORD_SIZE*2, 0 }, /* 10: GOTOFF_OFFSET. */
+ { 0x7fffffff, 0x80000000, INST_WORD_SIZE*2, 0 }, /* 11: TLSGD_OFFSET. */
+ { 0x7fffffff, 0x80000000, INST_WORD_SIZE*2, 0 }, /* 12: TLSLD_OFFSET. */
+ { 0x7fffffff, 0x80000000, INST_WORD_SIZE*1, 0 }, /* 13: TLSDTPMOD_OFFSET. */
+ { 0x7fffffff, 0x80000000, INST_WORD_SIZE*2, 0 }, /* 14: TLSDTPREL_OFFSET. */
+ { 0x7fffffff, 0x80000000, INST_WORD_SIZE*2, 0 }, /* 15: TLSGOTTPREL_OFFSET. */
+ { 0x7fffffff, 0x80000000, INST_WORD_SIZE*2, 0 } /* 16: TLSTPREL_OFFSET. */
};
static struct hash_control * opcode_hash_control; /* Opcode mnemonics. */
}
/* Symbol modifiers (@GOT, @PLT, @GOTOFF). */
+#define IMM_NONE 0
#define IMM_GOT 1
#define IMM_PLT 2
#define IMM_GOTOFF 3
+#define IMM_TLSGD 4
+#define IMM_TLSLD 5
+#define IMM_TLSDTPMOD 6
+#define IMM_TLSDTPREL 7
+#define IMM_TLSTPREL 8
+#define IMM_MAX 9
+
+struct imm_type {
+ char *isuffix; /* Suffix String */
+ int itype; /* Suffix Type */
+ int otype; /* Offset Type */
+};
+
+/* These are NOT in assending order of type, GOTOFF is ahead to make
+ sure @GOTOFF does not get matched with @GOT */
+static struct imm_type imm_types[] = {
+ { "NONE", IMM_NONE , 0 },
+ { "GOTOFF", IMM_GOTOFF , GOTOFF_OFFSET },
+ { "GOT", IMM_GOT , GOT_OFFSET },
+ { "PLT", IMM_PLT , PLT_OFFSET },
+ { "TLSGD", IMM_TLSGD , TLSGD_OFFSET },
+ { "TLSLDM", IMM_TLSLD, TLSLD_OFFSET },
+ { "TLSDTPMOD", IMM_TLSDTPMOD, TLSDTPMOD_OFFSET },
+ { "TLSDTPREL", IMM_TLSDTPREL, TLSDTPREL_OFFSET },
+ { "TLSTPREL", IMM_TLSTPREL, TLSTPREL_OFFSET }
+};
+
+static int
+match_imm (const char *s, int *ilen)
+{
+ int i;
+ int slen;
+
+ /* Check for matching suffix */
+ for (i = 1; i < IMM_MAX; i++)
+ {
+ slen = strlen (imm_types[i].isuffix);
+
+ if (strncmp (imm_types[i].isuffix, s, slen) == 0)
+ {
+ *ilen = slen;
+ return imm_types[i].itype;
+ }
+ } /* for */
+ *ilen = 0;
+ return 0;
+}
+
+static int
+get_imm_otype (int itype)
+{
+ int i, otype;
+
+ otype = 0;
+ /* Check for matching itype */
+ for (i = 1; i < IMM_MAX; i++)
+ {
+ if (imm_types[i].itype == itype)
+ {
+ otype = imm_types[i].otype;
+ break;
+ }
+ }
+ return otype;
+}
static symbolS * GOT_symbol;
{
char *new_pointer;
char *atp;
+ int itype, ilen;
+
+ ilen = 0;
/* Find the start of "@GOT" or "@PLT" suffix (if any) */
for (atp = s; *atp != '@'; atp++)
if (*atp == '@')
{
- if (strncmp (atp + 1, "GOTOFF", 5) == 0)
- {
- *atp = 0;
- e->X_md = IMM_GOTOFF;
- }
- else if (strncmp (atp + 1, "GOT", 3) == 0)
- {
- *atp = 0;
- e->X_md = IMM_GOT;
- }
- else if (strncmp (atp + 1, "PLT", 3) == 0)
- {
- *atp = 0;
- e->X_md = IMM_PLT;
- }
+ itype = match_imm (atp + 1, &ilen);
+ if (itype != 0)
+ {
+ *atp = 0;
+ e->X_md = itype;
+ }
else
- {
- atp = NULL;
- e->X_md = 0;
- }
+ {
+ atp = NULL;
+ e->X_md = 0;
+ ilen = 0;
+ }
*atp = 0;
}
else
new_pointer = parse_exp (s, e);
+ if (!GOT_symbol && ! strncmp (s, GOT_SYMBOL_NAME, 20))
+ {
+ GOT_symbol = symbol_find_or_make (GOT_SYMBOL_NAME);
+ }
+
if (e->X_op == O_absent)
; /* An error message has already been emitted. */
else if ((e->X_op != O_constant && e->X_op != O_symbol) )
{
*atp = '@'; /* restore back (needed?) */
if (new_pointer >= atp)
- new_pointer += (e->X_md == IMM_GOTOFF)?7:4;
- /* sizeof("@GOTOFF", "@GOT" or "@PLT") */
-
+ new_pointer += ilen + 1; /* sizeof (imm_suffix) + 1 for '@' */
}
return new_pointer;
}
if (fixP->fx_r_type == BFD_RELOC_MICROBLAZE_64_GOTOFF
|| fixP->fx_r_type == BFD_RELOC_MICROBLAZE_32_GOTOFF
|| fixP->fx_r_type == BFD_RELOC_MICROBLAZE_64_GOT
- || fixP->fx_r_type == BFD_RELOC_MICROBLAZE_64_PLT)
+ || fixP->fx_r_type == BFD_RELOC_MICROBLAZE_64_PLT
+ || fixP->fx_r_type == BFD_RELOC_MICROBLAZE_64_TLSGD
+ || fixP->fx_r_type == BFD_RELOC_MICROBLAZE_64_TLSLD
+ || fixP->fx_r_type == BFD_RELOC_MICROBLAZE_32_TLSDTPMOD
+ || fixP->fx_r_type == BFD_RELOC_MICROBLAZE_32_TLSDTPREL
+ || fixP->fx_r_type == BFD_RELOC_MICROBLAZE_64_TLSDTPREL
+ || fixP->fx_r_type == BFD_RELOC_MICROBLAZE_64_TLSGOTTPREL
+ || fixP->fx_r_type == BFD_RELOC_MICROBLAZE_64_TLSTPREL)
return 0;
return 1;
opc = str_microblaze_rw_anchor;
else
opc = NULL;
- if (exp.X_md == IMM_GOT)
- subtype = GOT_OFFSET;
- else if (exp.X_md == IMM_PLT)
- subtype = PLT_OFFSET;
- else if (exp.X_md == IMM_GOTOFF)
- subtype = GOTOFF_OFFSET;
+ if (exp.X_md != 0)
+ subtype = get_imm_otype(exp.X_md);
else
subtype = opcode->inst_offset_type;
char *opc = NULL;
relax_substateT subtype;
- if (exp.X_md == IMM_GOT)
- subtype = GOT_OFFSET;
- else if (exp.X_md == IMM_PLT)
- subtype = PLT_OFFSET;
+ if (exp.X_md != 0)
+ subtype = get_imm_otype(exp.X_md);
else
subtype = opcode->inst_offset_type;
+
output = frag_var (rs_machine_dependent,
isize * 2, /* maxm of 2 words. */
isize, /* minm of 1 word. */
char *opc = NULL;
relax_substateT subtype;
- if (exp.X_md == IMM_GOT)
- subtype = GOT_OFFSET;
- else if (exp.X_md == IMM_PLT)
- subtype = PLT_OFFSET;
- else
+ if (exp.X_md != 0)
+ subtype = get_imm_otype(exp.X_md);
+ else
subtype = opcode->inst_offset_type;
+
output = frag_var (rs_machine_dependent,
isize * 2, /* maxm of 2 words. */
isize, /* minm of 1 word. */
char *opc = NULL;
relax_substateT subtype;
- if (exp.X_md == IMM_GOT)
- subtype = GOT_OFFSET;
- else if (exp.X_md == IMM_PLT)
- subtype = PLT_OFFSET;
- else
- subtype = opcode->inst_offset_type;
+ if (exp.X_md != 0)
+ subtype = get_imm_otype(exp.X_md);
+ else
+ subtype = opcode->inst_offset_type;
+
output = frag_var (rs_machine_dependent,
isize * 2, /* maxm of 2 words. */
isize, /* minm of 1 word. */
fragP->fr_fix += INST_WORD_SIZE * 2;
fragP->fr_var = 0;
break;
+ case TLSGD_OFFSET:
+ fix_new (fragP, fragP->fr_fix, INST_WORD_SIZE * 2, fragP->fr_symbol,
+ fragP->fr_offset, FALSE, BFD_RELOC_MICROBLAZE_64_TLSGD);
+ fragP->fr_fix += INST_WORD_SIZE * 2;
+ fragP->fr_var = 0;
+ break;
+ case TLSLD_OFFSET:
+ fix_new (fragP, fragP->fr_fix, INST_WORD_SIZE * 2, fragP->fr_symbol,
+ fragP->fr_offset, FALSE, BFD_RELOC_MICROBLAZE_64_TLSLD);
+ fragP->fr_fix += INST_WORD_SIZE * 2;
+ fragP->fr_var = 0;
+ break;
+ case TLSDTPREL_OFFSET:
+ fix_new (fragP, fragP->fr_fix, INST_WORD_SIZE * 2, fragP->fr_symbol,
+ fragP->fr_offset, FALSE, BFD_RELOC_MICROBLAZE_64_TLSDTPREL);
+ fragP->fr_fix += INST_WORD_SIZE * 2;
+ fragP->fr_var = 0;
+ break;
default:
abort ();
}
break;
+ case BFD_RELOC_MICROBLAZE_64_TLSDTPREL:
+ case BFD_RELOC_MICROBLAZE_64_TLSGD:
+ case BFD_RELOC_MICROBLAZE_64_TLSLD:
+ S_SET_THREAD_LOCAL (fixP->fx_addsy);
+
case BFD_RELOC_MICROBLAZE_64_GOTPC:
case BFD_RELOC_MICROBLAZE_64_GOT:
case BFD_RELOC_MICROBLAZE_64_PLT:
case GOT_OFFSET:
case PLT_OFFSET:
case GOTOFF_OFFSET:
+ case TLSGD_OFFSET:
+ case TLSLD_OFFSET:
+ case TLSTPREL_OFFSET:
+ case TLSDTPREL_OFFSET:
fragP->fr_var = INST_WORD_SIZE*2;
break;
case DEFINED_RO_SEGMENT:
case DEFINED_RW_SEGMENT:
case DEFINED_PC_OFFSET:
+ case TLSDTPMOD_OFFSET:
fragP->fr_var = INST_WORD_SIZE;
break;
default:
case BFD_RELOC_MICROBLAZE_64_PLT:
case BFD_RELOC_MICROBLAZE_64_GOTOFF:
case BFD_RELOC_MICROBLAZE_32_GOTOFF:
+ case BFD_RELOC_MICROBLAZE_64_TLSGD:
+ case BFD_RELOC_MICROBLAZE_64_TLSLD:
+ case BFD_RELOC_MICROBLAZE_32_TLSDTPMOD:
+ case BFD_RELOC_MICROBLAZE_32_TLSDTPREL:
+ case BFD_RELOC_MICROBLAZE_64_TLSDTPREL:
+ case BFD_RELOC_MICROBLAZE_64_TLSGOTTPREL:
+ case BFD_RELOC_MICROBLAZE_64_TLSTPREL:
code = fixp->fx_r_type;
break;