aboutsummaryrefslogtreecommitdiff
path: root/bfd/elf32-microblaze.c
diff options
context:
space:
mode:
Diffstat (limited to 'bfd/elf32-microblaze.c')
-rw-r--r--bfd/elf32-microblaze.c629
1 files changed, 508 insertions, 121 deletions
diff --git a/bfd/elf32-microblaze.c b/bfd/elf32-microblaze.c
index be2de13..9d0a7ca 100644
--- a/bfd/elf32-microblaze.c
+++ b/bfd/elf32-microblaze.c
@@ -370,6 +370,132 @@ static reloc_howto_type microblaze_elf_howto_raw[] =
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
@@ -461,6 +587,27 @@ microblaze_elf_reloc_type_lookup (bfd * abfd ATTRIBUTE_UNUSED,
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;
@@ -550,8 +697,21 @@ struct elf32_mb_link_hash_entry
/* 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. */
@@ -571,8 +731,17 @@ struct elf32_mb_link_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) \
@@ -604,6 +773,7 @@ link_hash_newfunc (struct bfd_hash_entry *entry,
eh = (struct elf32_mb_link_hash_entry *) entry;
eh->dyn_relocs = NULL;
+ eh->tls_mask = 0;
}
return entry;
@@ -654,6 +824,40 @@ microblaze_elf_final_sdp (struct bfd_link_info *info)
+ 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],
@@ -707,6 +911,7 @@ microblaze_elf_relocate_section (bfd *output_bfd,
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 ();
@@ -738,6 +943,8 @@ microblaze_elf_relocate_section (bfd *output_bfd,
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"),
@@ -971,70 +1178,182 @@ microblaze_elf_relocate_section (bfd *output_bfd,
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;
}
@@ -1064,6 +1383,14 @@ microblaze_elf_relocate_section (bfd *output_bfd,
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:
@@ -1941,6 +2268,33 @@ create_got_section (bfd *dynobj, struct bfd_link_info *info)
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
@@ -1977,6 +2331,7 @@ microblaze_elf_check_relocs (bfd * abfd,
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);
@@ -2012,6 +2367,13 @@ microblaze_elf_check_relocs (bfd * abfd,
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)
{
@@ -2023,25 +2385,12 @@ microblaze_elf_check_relocs (bfd * abfd,
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;
@@ -2105,45 +2454,16 @@ microblaze_elf_check_relocs (bfd * abfd,
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
@@ -2274,6 +2594,8 @@ microblaze_elf_copy_indirect_symbol (struct bfd_link_info *info,
eind->dyn_relocs = NULL;
}
+ edir->tls_mask |= eind->tls_mask;
+
_bfd_elf_link_hash_copy_indirect (info, dir, ind);
}
@@ -2492,8 +2814,10 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void * dat)
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.
@@ -2505,15 +2829,43 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void * dat)
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;
@@ -2611,6 +2963,7 @@ microblaze_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
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)
@@ -2650,17 +3003,36 @@ microblaze_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
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;
@@ -2671,6 +3043,16 @@ microblaze_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
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. */
@@ -2789,6 +3171,7 @@ microblaze_elf_finish_dynamic_symbol (bfd *output_bfd,
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)
@@ -2860,12 +3243,14 @@ microblaze_elf_finish_dynamic_symbol (bfd *output_bfd,
}
}
- 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. */
@@ -2874,8 +3259,7 @@ microblaze_elf_finish_dynamic_symbol (bfd *output_bfd,
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
@@ -2888,22 +3272,25 @@ microblaze_elf_finish_dynamic_symbol (bfd *output_bfd,
&& 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)
@@ -3073,7 +3460,7 @@ microblaze_elf_add_symbol_hook (bfd *abfd,
#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