aboutsummaryrefslogtreecommitdiff
path: root/bfd/arc-got.h
diff options
context:
space:
mode:
authorCupertino Miranda <cmiranda@synopsys.com>2016-06-14 22:55:44 +0200
committerClaudiu Zissulescu <claziss@synopsys.com>2016-07-11 15:24:35 +0200
commit08759e0fc8b0de1c56ad388212a104f3a6d61c25 (patch)
treefccc3f881e487bed7755dcfe11bf14b74ffef238 /bfd/arc-got.h
parent36897971c8d022d5c28cc8af4b2f1df04a7e964f (diff)
downloadgdb-08759e0fc8b0de1c56ad388212a104f3a6d61c25.zip
gdb-08759e0fc8b0de1c56ad388212a104f3a6d61c25.tar.gz
gdb-08759e0fc8b0de1c56ad388212a104f3a6d61c25.tar.bz2
Fixes done to TLS.
TLS relocations did not support multiple TLS modes for the same symbol in a single object file. Refactored how GOT and TLS is implemented. Removed code duplications between local and global symbols conditioning. bfd/ChangeLog: 2016-06-14 Cupertino Miranda <cmiranda@synopsys.com> * arc-got.h: Moved got related structures from elf32-arc.c to this file. More precisely, tls_type_e, tls_got_entries, got_entry. * (arc_get_local_got_ents, got_entry_for_type, new_got_entry_to_list, tls_type_for_reloc, symbol_has_entry_of_type, get_got_entry_list_for_symbol, arc_got_entry_type_for_reloc, ADD_SYMBOL_REF_SEC_AND_RELOC, arc_fill_got_info_for_reloc, relocate_fix_got_relocs_for_got_info, create_got_dynrelocs_for_single_entry, create_got_dynrelocs_for_got_info): Added to file. * elf32-arc.c: Removed GOT & TLS related structs and functions to arc-got.h. Signed-off-by: Claudiu Zissulescu <claziss@synopsys.com>
Diffstat (limited to 'bfd/arc-got.h')
-rw-r--r--bfd/arc-got.h511
1 files changed, 511 insertions, 0 deletions
diff --git a/bfd/arc-got.h b/bfd/arc-got.h
new file mode 100644
index 0000000..9e8fd10
--- /dev/null
+++ b/bfd/arc-got.h
@@ -0,0 +1,511 @@
+/* ARC-specific support for 32-bit ELF
+ Copyright (C) 1994-2016 Free Software Foundation, Inc.
+ Contributed by Cupertino Miranda (cmiranda@synopsys.com).
+
+ This file is part of BFD, the Binary File Descriptor library.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+ MA 02110-1301, USA. */
+
+#ifndef ARC_GOT_H
+#define ARC_GOT_H
+
+enum tls_type_e
+{
+ GOT_UNKNOWN = 0,
+ GOT_NORMAL,
+ GOT_TLS_GD,
+ GOT_TLS_IE,
+ GOT_TLS_LE
+};
+
+enum tls_got_entries
+{
+ TLS_GOT_NONE = 0,
+ TLS_GOT_MOD,
+ TLS_GOT_OFF,
+ TLS_GOT_MOD_AND_OFF
+};
+
+struct got_entry
+{
+ struct got_entry *next;
+ enum tls_type_e type;
+ bfd_vma offset;
+ bfd_boolean processed;
+ bfd_boolean created_dyn_relocation;
+ enum tls_got_entries existing_entries;
+};
+
+static struct got_entry **
+arc_get_local_got_ents (bfd * abfd)
+{
+ static struct got_entry **local_got_ents = NULL;
+
+ if (local_got_ents == NULL)
+ {
+ size_t size;
+ Elf_Internal_Shdr *symtab_hdr = &((elf_tdata (abfd))->symtab_hdr);
+
+ size = symtab_hdr->sh_info * sizeof (bfd_vma);
+ local_got_ents = (struct got_entry **)
+ bfd_alloc (abfd, sizeof (struct got_entry *) * size);
+ if (local_got_ents == NULL)
+ return FALSE;
+
+ memset (local_got_ents, 0, sizeof (struct got_entry *) * size);
+ elf_local_got_ents (abfd) = local_got_ents;
+ }
+
+ return local_got_ents;
+}
+
+static struct got_entry *
+got_entry_for_type (struct got_entry **list,
+ enum tls_type_e type)
+{
+ struct got_entry **p = list;
+ while (*p != NULL)
+ {
+ if ((*p)->type == type)
+ return *p;
+ p = &((*p)->next);
+ }
+ return NULL;
+}
+
+static void
+new_got_entry_to_list (struct got_entry **list,
+ enum tls_type_e type,
+ bfd_vma offset,
+ enum tls_got_entries existing_entries)
+{
+ /* Find list end. Avoid having multiple entries of the same
+ type. */
+ struct got_entry **p = list;
+ while (*p != NULL)
+ {
+ if ((*p)->type == type)
+ return;
+ p = &((*p)->next);
+ }
+
+ struct got_entry *entry
+ = (struct got_entry *) malloc (sizeof (struct got_entry));
+
+ entry->type = type;
+ entry->offset = offset;
+ entry->next = NULL;
+ entry->processed = FALSE;
+ entry->created_dyn_relocation = FALSE;
+ entry->existing_entries = existing_entries;
+
+ ARC_DEBUG ("New GOT got entry added to list: "
+ "type: %d, offset: %d, existing_entries:%d\n",
+ type, offset, existing_entries);
+
+ /* Add the entry to the end of the list. */
+ *p = entry;
+}
+
+static enum tls_type_e
+tls_type_for_reloc (reloc_howto_type *howto)
+{
+ enum tls_type_e ret = GOT_UNKNOWN;
+ if (is_reloc_for_GOT (howto))
+ ret = GOT_NORMAL;
+ else
+ {
+ switch (howto->type)
+ {
+ case R_ARC_TLS_GD_GOT:
+ ret = GOT_TLS_GD;
+ break;
+ case R_ARC_TLS_IE_GOT:
+ ret = GOT_TLS_IE;
+ break;
+ case R_ARC_TLS_LE_32:
+ ret = GOT_TLS_LE;
+ break;
+ default:
+ ret = GOT_UNKNOWN;
+ break;
+ }
+ }
+ return ret;
+};
+
+static struct got_entry **
+get_got_entry_list_for_symbol (bfd *abfd,
+ unsigned long r_symndx,
+ struct elf_link_hash_entry *h)
+{
+ if (h != NULL)
+ {
+ return &h->got.glist;
+ }
+ else
+ {
+ struct got_entry **local_got_ents
+ = arc_get_local_got_ents (abfd);
+ return &local_got_ents[r_symndx];
+ }
+}
+
+
+static enum tls_type_e
+arc_got_entry_type_for_reloc (reloc_howto_type *howto)
+{
+ enum tls_type_e type = GOT_UNKNOWN;
+
+ if (is_reloc_for_GOT (howto))
+ type = GOT_NORMAL;
+ else if (is_reloc_for_TLS (howto))
+ {
+ switch (howto->type)
+ {
+ case R_ARC_TLS_GD_GOT:
+ type = GOT_TLS_GD;
+ break;
+ case R_ARC_TLS_IE_GOT:
+ type = GOT_TLS_IE;
+ break;
+ default:
+ break;
+ }
+ }
+ return type;
+}
+
+#define ADD_SYMBOL_REF_SEC_AND_RELOC(SECNAME, COND_FOR_RELOC, H) \
+ htab->s##SECNAME->size; \
+ { \
+ if (COND_FOR_RELOC) \
+ { \
+ htab->srel##SECNAME->size += sizeof (Elf32_External_Rela); \
+ ARC_DEBUG ("arc_info: Added reloc space in " \
+ #SECNAME " section at " __FILE__ \
+ ":%d for symbol %s\n", \
+ __LINE__, name_for_global_symbol (H)); \
+ } \
+ if (H) \
+ if (h->dynindx == -1 && !h->forced_local) \
+ if (! bfd_elf_link_record_dynamic_symbol (info, H)) \
+ return FALSE; \
+ htab->s##SECNAME->size += 4; \
+ } \
+
+static bfd_boolean
+arc_fill_got_info_for_reloc (enum tls_type_e type,
+ struct got_entry **list,
+ struct bfd_link_info * info,
+ struct elf_link_hash_entry *h)
+{
+ struct elf_link_hash_table *htab = elf_hash_table (info);
+
+ if (got_entry_for_type (list, type) != NULL)
+ return TRUE;
+
+ switch (type)
+ {
+ case GOT_NORMAL:
+ {
+ bfd_vma offset
+ = ADD_SYMBOL_REF_SEC_AND_RELOC (got, bfd_link_pic (info)
+ || h != NULL, h);
+ new_got_entry_to_list (list, type, offset, TLS_GOT_NONE);
+ }
+ break;
+
+
+ case GOT_TLS_GD:
+ {
+ bfd_vma offset
+ = ADD_SYMBOL_REF_SEC_AND_RELOC (got, TRUE, h);
+ bfd_vma ATTRIBUTE_UNUSED notneeded
+ = ADD_SYMBOL_REF_SEC_AND_RELOC (got, TRUE, h);
+ new_got_entry_to_list (list, type, offset, TLS_GOT_MOD_AND_OFF);
+ }
+ break;
+ case GOT_TLS_IE:
+ case GOT_TLS_LE:
+ {
+ bfd_vma offset
+ = ADD_SYMBOL_REF_SEC_AND_RELOC (got, TRUE, h);
+ new_got_entry_to_list (list, type, offset, TLS_GOT_OFF);
+ }
+ break;
+
+ default:
+ return FALSE;
+ break;
+ }
+ return TRUE;
+}
+
+
+static bfd_vma
+relocate_fix_got_relocs_for_got_info (struct got_entry **list_p,
+ enum tls_type_e type,
+ struct bfd_link_info * info,
+ bfd *output_bfd,
+ unsigned long r_symndx,
+ Elf_Internal_Sym *local_syms,
+ asection **local_sections,
+ struct elf_link_hash_entry *h,
+ struct arc_relocation_data *reloc_data)
+{
+
+ if (list_p == NULL || type == GOT_UNKNOWN || type == GOT_TLS_LE)
+ return 0;
+
+ struct elf_link_hash_table *htab = elf_hash_table (info);
+ struct got_entry *entry = NULL;
+
+ entry = got_entry_for_type (list_p, type);
+ BFD_ASSERT (entry);
+
+ if (h == NULL
+ || (! elf_hash_table (info)->dynamic_sections_created
+ || (bfd_link_pic (info)
+ && SYMBOL_REFERENCES_LOCAL (info, h))))
+ {
+ const char ATTRIBUTE_UNUSED *symbol_name;
+ static const char local_name[] = "(local)";
+ asection *tls_sec = NULL;
+ bfd_vma sym_value = 0;
+
+ if (h != NULL)
+ {
+ // TODO: This should not be here.
+ reloc_data->sym_value = h->root.u.def.value;
+ reloc_data->sym_section = h->root.u.def.section;
+
+ sym_value = h->root.u.def.value
+ + h->root.u.def.section->output_section->vma
+ + h->root.u.def.section->output_offset;
+
+ tls_sec = elf_hash_table (info)->tls_sec;
+
+ symbol_name = h->root.root.string;
+ }
+ else
+ {
+ Elf_Internal_Sym *sym = local_syms + r_symndx;
+ asection *sec = local_sections[r_symndx];
+
+ sym_value = sym->st_value
+ + sec->output_section->vma
+ + sec->output_offset;
+
+ tls_sec = elf_hash_table (info)->tls_sec;
+
+ symbol_name = local_name;
+ }
+
+
+ if (entry && entry->processed == FALSE)
+ {
+ switch (entry->type)
+ {
+ case GOT_TLS_GD:
+ {
+ BFD_ASSERT (tls_sec && tls_sec->output_section);
+ bfd_vma sec_vma = tls_sec->output_section->vma;
+
+ bfd_put_32 (output_bfd,
+ sym_value - sec_vma,
+ htab->sgot->contents + entry->offset
+ + (entry->existing_entries == TLS_GOT_MOD_AND_OFF
+ ? 4 : 0));
+
+ ARC_DEBUG ("arc_info: FIXED -> %s value = 0x%x "
+ "@ 0x%x, for symbol %s\n",
+ (entry->type == GOT_TLS_GD ? "GOT_TLS_GD" :
+ "GOT_TLS_IE"),
+ sym_value - sec_vma,
+ htab->sgot->contents + entry->offset
+ + (entry->existing_entries == TLS_GOT_MOD_AND_OFF
+ ? 4 : 0),
+ symbol_name);
+ }
+ break;
+
+ case GOT_TLS_IE:
+ {
+ BFD_ASSERT (tls_sec && tls_sec->output_section);
+ bfd_vma ATTRIBUTE_UNUSED sec_vma
+ = tls_sec->output_section->vma;
+
+ ARC_DEBUG ("arc_info: FIXED -> %s value = 0x%x "
+ "@ 0x%x, for symbol %s\n",
+ (entry->type == GOT_TLS_GD ? "GOT_TLS_GD" :
+ "GOT_TLS_IE"),
+ sym_value - sec_vma,
+ htab->sgot->contents + entry->offset
+ + (entry->existing_entries == TLS_GOT_MOD_AND_OFF
+ ? 4 : 0),
+ symbol_name);
+ }
+ break;
+
+ case GOT_NORMAL:
+ {
+ bfd_vma sec_vma
+ = reloc_data->sym_section->output_section->vma
+ + reloc_data->sym_section->output_offset;
+
+ if (h->root.type != bfd_link_hash_undefweak)
+ {
+ bfd_put_32 (output_bfd,
+ reloc_data->sym_value + sec_vma,
+ htab->sgot->contents + entry->offset);
+
+ ARC_DEBUG ("arc_info: PATCHED: 0x%08x "
+ "@ 0x%08x for sym %s in got offset 0x%x\n",
+ reloc_data->sym_value + sec_vma,
+ htab->sgot->output_section->vma
+ + htab->sgot->output_offset + entry->offset,
+ symbol_name,
+ entry->offset);
+ }
+ else
+ {
+ ARC_DEBUG ("arc_info: PATCHED: NOT_PATCHED "
+ "@ 0x%08x for sym %s in got offset 0x%x "
+ "(is undefweak)\n",
+ htab->sgot->output_section->vma
+ + htab->sgot->output_offset + entry->offset,
+ symbol_name,
+ entry->offset);
+ }
+ }
+ break;
+ default:
+ BFD_ASSERT (0);
+ break;
+ }
+ entry->processed = TRUE;
+ }
+ }
+
+ return entry->offset;
+}
+
+static void
+create_got_dynrelocs_for_single_entry (struct got_entry *list,
+ bfd *output_bfd,
+ struct bfd_link_info * info,
+ struct elf_link_hash_entry *h)
+{
+ if (list == NULL)
+ return;
+
+ bfd_vma got_offset = list->offset;
+
+ if (list->type == GOT_NORMAL
+ && list->created_dyn_relocation == FALSE)
+ {
+ if (bfd_link_pic (info)
+ && h != NULL
+ && (info->symbolic || h->dynindx == -1)
+ && h->def_regular)
+ {
+ ADD_RELA (output_bfd, got, got_offset, 0, R_ARC_RELATIVE, 0);
+ }
+ /* Do not fully understand the side effects of this condition.
+ The relocation space might still being reserved. Perhaps
+ I should clear its value. */
+ else if (h != NULL && h->dynindx != -1)
+ {
+ ADD_RELA (output_bfd, got, got_offset, h->dynindx, R_ARC_GLOB_DAT, 0);
+ }
+ list->created_dyn_relocation = TRUE;
+ }
+ else if (list->existing_entries != TLS_GOT_NONE
+ && list->created_dyn_relocation == FALSE)
+ {
+ /* TODO TLS: This is not called for local symbols.
+ In order to correctly implement TLS, this should also
+ be called for all local symbols with tls got entries.
+ Should be moved to relocate_section in order to make it
+ work for local symbols. */
+ struct elf_link_hash_table *htab = elf_hash_table (info);
+ enum tls_got_entries e = list->existing_entries;
+
+ BFD_ASSERT (list->type != GOT_TLS_GD
+ || list->existing_entries == TLS_GOT_MOD_AND_OFF);
+
+ bfd_vma dynindx = (h == NULL || h->dynindx == -1) ? 0 : h->dynindx;
+
+ if (e == TLS_GOT_MOD_AND_OFF || e == TLS_GOT_MOD)
+ {
+ ADD_RELA (output_bfd, got, got_offset, dynindx,
+ R_ARC_TLS_DTPMOD, 0);
+ ARC_DEBUG ("arc_info: TLS_DYNRELOC: type = %d, \
+GOT_OFFSET = 0x%x, GOT_VMA = 0x%x, INDEX = %d, ADDEND = 0x%x\n",
+ list->type,
+ got_offset,
+ htab->sgot->output_section->vma
+ + htab->sgot->output_offset + got_offset,
+ dynindx, 0);
+ }
+ if (e == TLS_GOT_MOD_AND_OFF || e == TLS_GOT_OFF)
+ {
+ bfd_vma addend = 0;
+ if (list->type == GOT_TLS_IE)
+ addend = bfd_get_32 (output_bfd,
+ htab->sgot->contents + got_offset);
+
+ ADD_RELA (output_bfd, got,
+ got_offset + (e == TLS_GOT_MOD_AND_OFF ? 4 : 0),
+ dynindx,
+ (list->type == GOT_TLS_IE ? R_ARC_TLS_TPOFF
+ : R_ARC_TLS_DTPOFF),
+ addend);
+
+ ARC_DEBUG ("arc_info: TLS_DYNRELOC: type = %d, \
+GOT_OFFSET = 0x%x, GOT_VMA = 0x%x, INDEX = %d, ADDEND = 0x%x\n",
+ list->type,
+ got_offset,
+ htab->sgot->output_section->vma
+ + htab->sgot->output_offset + got_offset,
+ dynindx, addend);
+ }
+ list->created_dyn_relocation = TRUE;
+ }
+}
+
+static void
+create_got_dynrelocs_for_got_info (struct got_entry **list_p,
+ bfd *output_bfd,
+ struct bfd_link_info * info,
+ struct elf_link_hash_entry *h)
+{
+ if (list_p == NULL)
+ return;
+
+ struct got_entry *list = *list_p;
+ /* Traverse the list of got entries for this symbol. */
+ while (list)
+ {
+ create_got_dynrelocs_for_single_entry (list, output_bfd, info, h);
+ list = list->next;
+ }
+}
+
+#undef ADD_SYMBOL_REF_SEC_AND_RELOC
+
+#endif /* ARC_GOT_H */